/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.notepad;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.LiveFolders;
import android.text.TextUtils;
import android.util.Log;

import com.example.android.notepad.NotePad.NoteColumns;

import de.fuberlin.replication.Replication;

/**
 * Provides access to a database of notes. Each note has a title, the note itself, a creation date and a modified data.
 */
public class NotePadProvider extends ContentProvider {

	private static final String TAG = "NotePadProvider";

	private static final String DATABASE_NAME = "notepad.db";
	private static final int DATABASE_VERSION = 2;
	private static final String NOTES_TABLE_NAME = "notes";

	private static HashMap<String, String> sNotesProjectionMap;
	private static HashMap<String, String> sLiveFolderProjectionMap;

	private static final int NOTES = 1;
	private static final int NOTE_ID = 2;
	private static final int LIVE_FOLDER_NOTES = 3;

	private static final UriMatcher sUriMatcher;

	/**
	 * This class helps open, create, and upgrade the database file.
	 */
	private static class DatabaseHelper extends SQLiteOpenHelper {

		DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
					+ BaseColumns._ID + " INTEGER PRIMARY KEY,"
					+ NoteColumns.TITLE + " TEXT,"
					+ NoteColumns.NOTE + " TEXT,"
					+ NoteColumns.CREATED_DATE + " INTEGER,"
					+ NoteColumns.MODIFIED_DATE + " INTEGER"
					+ ");");
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
					+ newVersion + ", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS notes");
			onCreate(db);
		}
	}

	private DatabaseHelper mOpenHelper;

	private NotePadSynchronizable synchronizable;

	@Override
	public boolean onCreate() {
		mOpenHelper = new DatabaseHelper(getContext());

		synchronizable = new NotePadSynchronizable(getContext());

		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		qb.setTables(NOTES_TABLE_NAME);

		switch (sUriMatcher.match(uri)) {
			case NOTES:
				qb.setProjectionMap(sNotesProjectionMap);
				break;

			case NOTE_ID:
				qb.setProjectionMap(sNotesProjectionMap);
				qb.appendWhere(BaseColumns._ID + "=" + uri.getPathSegments().get(1));
				break;

			case LIVE_FOLDER_NOTES:
				qb.setProjectionMap(sLiveFolderProjectionMap);
				break;

			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}

		// If no sort order is specified use the default
		String orderBy;
		if (TextUtils.isEmpty(sortOrder))
			orderBy = NoteColumns.DEFAULT_SORT_ORDER;
		else
			orderBy = sortOrder;

		// Get the database and run the query
		SQLiteDatabase db = mOpenHelper.getReadableDatabase();
		Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);

		// Tell the cursor what uri to watch, so it knows when its source data changes
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	@Override
	public String getType(Uri uri) {
		switch (sUriMatcher.match(uri)) {
			case NOTES:
			case LIVE_FOLDER_NOTES:
				return NoteColumns.CONTENT_TYPE;

			case NOTE_ID:
				return NoteColumns.CONTENT_ITEM_TYPE;

			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		// Validate the requested uri
		if (sUriMatcher.match(uri) != NOTES)
			throw new IllegalArgumentException("Unknown URI " + uri);

		ContentValues values;
		if (initialValues != null)
			values = new ContentValues(initialValues);
		else
			values = new ContentValues();

		Long now = Long.valueOf(System.currentTimeMillis());

		// Make sure that the fields are all set
		if (values.containsKey(NoteColumns.CREATED_DATE) == false)
			values.put(NoteColumns.CREATED_DATE, now);

		if (values.containsKey(NoteColumns.MODIFIED_DATE) == false)
			values.put(NoteColumns.MODIFIED_DATE, now);

		if (values.containsKey(NoteColumns.TITLE) == false) {
			Resources r = Resources.getSystem();
			values.put(NoteColumns.TITLE, r.getString(android.R.string.untitled));
		}

		if (values.containsKey(NoteColumns.NOTE) == false)
			values.put(NoteColumns.NOTE, "");

		SQLiteDatabase db = mOpenHelper.getWritableDatabase();
		long rowId = db.insert(NOTES_TABLE_NAME, NoteColumns.NOTE, values);
		if (rowId > 0) {
			Uri noteUri = ContentUris.withAppendedId(NoteColumns.CONTENT_URI, rowId);
			getContext().getContentResolver().notifyChange(noteUri, null);
			return noteUri;
		}

		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		SQLiteDatabase db = mOpenHelper.getWritableDatabase();
		int count;
		switch (sUriMatcher.match(uri)) {
			case NOTES:
				count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
				break;

			case NOTE_ID:
				String noteId = uri.getPathSegments().get(1);
				count = db.delete(NOTES_TABLE_NAME, BaseColumns._ID + "=" + noteId
						+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
				break;

			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	@Override
	/**
	 * This method has been modified in order to work with the synchronization library. The modification makes sure that
	 * a call to the <code>update()</code> method of the library is performed, when needed.
	 */
	public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
		boolean update = true;

		if (values.containsKey(Note.UPDATE)) {
			update = values.getAsBoolean(Note.UPDATE).booleanValue();

			values.remove(Note.UPDATE);
		}

		SQLiteDatabase db = mOpenHelper.getWritableDatabase();
		int count;
		switch (sUriMatcher.match(uri)) {
			case NOTES:
				count = db.update(NOTES_TABLE_NAME, values, where, whereArgs);
				break;

			case NOTE_ID:
				String noteId = uri.getPathSegments().get(1);
				count = db.update(NOTES_TABLE_NAME, values, BaseColumns._ID + "=" + noteId
						+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
				break;

			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}

		if (update) {
			Cursor cursor = query(uri, new String[] {NoteColumns.TITLE}, where, whereArgs, null);

			if (cursor.moveToFirst())
				while (cursor.isAfterLast() == false) {
					Replication.update(synchronizable, cursor.getString(0));

					cursor.moveToNext();
				}

			cursor.close();
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	static {
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
		sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
		sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES);

		sNotesProjectionMap = new HashMap<String, String>();
		sNotesProjectionMap.put(BaseColumns._ID, BaseColumns._ID);
		sNotesProjectionMap.put(NoteColumns.TITLE, NoteColumns.TITLE);
		sNotesProjectionMap.put(NoteColumns.NOTE, NoteColumns.NOTE);
		sNotesProjectionMap.put(NoteColumns.CREATED_DATE, NoteColumns.CREATED_DATE);
		sNotesProjectionMap.put(NoteColumns.MODIFIED_DATE, NoteColumns.MODIFIED_DATE);

		// Support for Live Folders.
		sLiveFolderProjectionMap = new HashMap<String, String>();
		sLiveFolderProjectionMap.put(BaseColumns._ID, BaseColumns._ID + " AS " +
				BaseColumns._ID);
		sLiveFolderProjectionMap.put(LiveFolders.NAME, NoteColumns.TITLE + " AS " +
				LiveFolders.NAME);
		// Add more columns here for more robust Live Folders.
	}
}
